﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using CSRedis;
using System.Text.Json;

namespace RedisClientSample
{
    public class CSRedisCoreHelper : IDisposable
    {
        private static readonly Lazy<RedisHelper> LazyInstance = new Lazy<RedisHelper>(() => new RedisHelper(ConnectionString));
        public static RedisHelper Instance => LazyInstance.Value;

        private readonly CSRedisClient _client;
        private readonly Dictionary<string, CancellationTokenSource> _activeSubscriptions = new Dictionary<string, CancellationTokenSource>();
        private readonly Dictionary<string, CSRedisClient.SubscribeObject> _subscribeObjects = new Dictionary<string, CSRedisClient.SubscribeObject>();
        private bool _disposed = false;
        public static string ConnectionString = "localhost:6379,defaultDatabase=0,allowAdmin=true";

        /// <summary>
        /// 构造函数（自定义连接字符串）
        /// </summary>
        /// <param name="connectionString">Redis连接字符串。例如："localhost:6379,password=123456,defaultDatabase=0"</param>
        public CSRedisCoreHelper(string connectionString)
        {
            _client = new CSRedisClient(connectionString);
        }

        public static void SetConnectString(string connectionString)
        {
            ConnectionString = connectionString;
        }

        #region String 操作

        public bool Set(string key, string value, TimeSpan? expiry = null)
        {
            if (expiry.HasValue)
                return _client.Set(key, value, (int)expiry.Value.TotalSeconds);
            return _client.Set(key, value);
        }

        public string Get(string key)
        {
            return _client.Get(key);
        }

        public bool Remove(string key)
        {
            return _client.Del(key) > 0;
        }

        #endregion

        #region Hash 操作

        public bool HashSet<T>(string hashId, string key, T value)
        {
            return _client.HSet(hashId, key, SerializeValue(value));
        }

        public T HashGet<T>(string hashId, string key)
        {
            var value = _client.HGet(hashId, key);
            return DeserializeValue<T>(value);
        }

        public List<string> HashGetAll(string hashId)
        {
            var entries = _client.HGetAll(hashId);
            return entries.Values.ToList();
        }

        #endregion

        #region 消息队列专用方法（确保FIFO）

        /// <summary>
        /// 从队列接收消息（左侧出队）- 严格的FIFO
        /// 这是最关键的FIFO方法，确保先进先出
        /// </summary>
        public T QueuePop<T>(string queueName)
        {
            try
            {
                // 使用LPOP从左侧弹出（头部），这是FIFO的核心
                var value = _client.LPop(queueName);
                if (string.IsNullOrEmpty(value))
                    return default(T);

                return DeserializeValue<T>(value);
            }
            catch (Exception ex)
            {
                return default(T);
            }
        }

        /// <summary>
        /// 使用Redis事务确保FIFO的原子性操作
        /// </summary>
        public T QueuePopAtomic<T>(string queueName)
        {
            // CSRedisCore 使用 StartPipe 创建管道/事务
            var pipe = _client.StartPipe();
            pipe.LPop(queueName);
            var results = pipe.EndPipe();

            if (results != null && results.Length > 0)
            {
                var value = results[0] as string;
                if (!string.IsNullOrEmpty(value))
                    return DeserializeValue<T>(value);
            }

            return default(T);
        }

        /// <summary>
        /// 批量FIFO消息获取 - 保持严格的先进先出顺序
        /// </summary>
        public List<T> QueuePopMultiple<T>(string queueName, int count)
        {
            var messages = new List<T>();

            for (int i = 0; i < count; i++)
            {
                var value = _client.LPop(queueName);
                if (string.IsNullOrEmpty(value))
                    break;

                var message = DeserializeValue<T>(value);
                if (message != null && !message.Equals(default(T)))
                {
                    messages.Add(message);
                }
            }

            return messages;
        }

        /// <summary>
        /// 发送消息到队列（右侧入队）- FIFO
        /// </summary>
        public void QueuePush<T>(string queueName, T message)
        {
            // 使用RPUSH确保右侧添加（FIFO入队）
            _client.RPush(queueName, SerializeValue(message));
        }

        /// <summary>
        /// 安全的FIFO消息获取（带重试机制）
        /// </summary>
        public T QueuePopSafe<T>(string queueName, int maxRetries = 3)
        {
            for (int i = 0; i < maxRetries; i++)
            {
                try
                {
                    var result = QueuePop<T>(queueName);
                    if (result != null && !result.Equals(default(T)))
                        return result;

                    // 队列为空，返回默认值
                    return default(T);
                }
                catch (Exception ex)
                {
                    if (i == maxRetries - 1) throw; // 最后一次尝试仍然失败，抛出异常
                    Thread.Sleep(100); // 短暂等待后重试
                }
            }
            return default(T);
        }

        /// <summary>
        /// 查看队列头部的消息（不移除）- FIFO顺序
        /// </summary>
        public T QueuePeek<T>(string queueName)
        {
            try
            {
                // 使用LINDEX获取第一个元素（不移除）
                var value = _client.LIndex(queueName, 0);
                if (!string.IsNullOrEmpty(value))
                    return DeserializeValue<T>(value);
            }
            catch (Exception ex)
            {
                throw new Exception($"QueuePeek错误: {ex.Message}");
            }

            return default(T);
        }

        /// <summary>
        /// 获取队列中指定范围的消息（不移除）- FIFO顺序
        /// </summary>
        public List<T> QueueRange<T>(string queueName, int start = 0, int end = -1)
        {
            try
            {
                var values = _client.LRange(queueName, start, end);
                var result = new List<T>();

                foreach (var value in values)
                {
                    var message = DeserializeValue<T>(value);
                    if (message != null && !message.Equals(default(T)))
                    {
                        result.Add(message);
                    }
                }
                return result;
            }
            catch (Exception ex)
            {
                return new List<T>();
            }
        }

        /// <summary>
        /// 获取队列长度
        /// </summary>
        public long QueueLength(string queueName)
        {
            try
            {
                return _client.LLen(queueName);
            }
            catch (Exception ex)
            {
                return 0;
            }
        }

        /// <summary>
        /// 尝试从队列接收消息（非阻塞）- 安全版本，不会抛出异常
        /// </summary>
        /// <returns>是否成功获取到消息</returns>
        public bool TryQueuePop<T>(string queueName, out T message)
        {
            message = default(T);

            try
            {
                message = QueuePop<T>(queueName);
                return message != null && !message.Equals(default(T));
            }
            catch
            {
                return false;
            }
        }

        /// <summary>
        /// 查看队列头部的多个消息（不移除）- 非阻塞
        /// </summary>
        public List<T> QueuePeekMultiple<T>(string queueName, int count = 10)
        {
            return ListRange<T>(queueName, 0, Math.Min(count - 1, (int)ListLength(queueName) - 1));
        }

        /// <summary>
        /// 阻塞式从队列接收消息
        /// </summary>
        public T QueueBlockingPop<T>(string queueName, TimeSpan timeout)
        {
            return ListBlockingLeftPop<T>(queueName, timeout);
        }

        /// <summary>
        /// 批量阻塞式从队列接收消息
        /// </summary>
        public List<T> QueueBlockingPopMultiple<T>(string queueName, int count, TimeSpan timeout)
        {
            var messages = new List<T>();

            // 先尝试非阻塞获取
            var nonBlockingMessages = QueuePopMultiple<T>(queueName, count);
            messages.AddRange(nonBlockingMessages);

            // 如果数量不够，使用阻塞方式获取剩余消息
            if (messages.Count < count)
            {
                int remaining = count - messages.Count;
                for (int i = 0; i < remaining; i++)
                {
                    var message = QueueBlockingPop<T>(queueName, timeout);
                    if (message != null && !message.Equals(default(T)))
                    {
                        messages.Add(message);
                    }
                    else
                    {
                        break; // 超时或获取失败
                    }
                }
            }

            return messages;
        }

        /// <summary>
        /// 获取队列中所有消息（不移除）- 主要用于调试
        /// </summary>
        public List<T> QueueGetAll<T>(string queueName)
        {
            return ListGetAll<T>(queueName);
        }

        #endregion

        #region List 列表操作（基础方法）

        public void ListAdd<T>(string listId, T value)
        {
            _client.RPush(listId, SerializeValue(value));
        }

        /// <summary>
        /// 从列表右侧插入元素（入队）
        /// </summary>
        public void ListRightPush<T>(string listId, T value)
        {
            _client.RPush(listId, SerializeValue(value));
        }

        /// <summary>
        /// 从列表左侧插入元素
        /// </summary>
        public void ListLeftPush<T>(string listId, T value)
        {
            _client.LPush(listId, SerializeValue(value));
        }

        /// <summary>
        /// 从列表左侧弹出元素（出队 - FIFO）- 非阻塞
        /// </summary>
        public T ListLeftPop<T>(string listId)
        {
            var value = _client.LPop(listId);
            return DeserializeValue<T>(value);
        }

        /// <summary>
        /// 从列表右侧弹出元素（栈 - LIFO）- 非阻塞
        /// </summary>
        public T ListRightPop<T>(string listId)
        {
            var value = _client.RPop(listId);
            return DeserializeValue<T>(value);
        }

        /// <summary>
        /// 阻塞式从列表左侧弹出元素（支持超时）
        /// </summary>
        public T ListBlockingLeftPop<T>(string listId, TimeSpan timeout)
        {
            // CSRedisCore 支持阻塞操作
            var result = _client.BLPop((int)timeout.TotalSeconds, listId);
            if (!string.IsNullOrEmpty(result))
                return DeserializeValue<T>(result);
            return default(T);
        }

        /// <summary>
        /// 获取列表长度
        /// </summary>
        public long ListLength(string listId)
        {
            return _client.LLen(listId);
        }

        /// <summary>
        /// 获取列表指定范围内的元素（不移除）
        /// </summary>
        public List<T> ListRange<T>(string listId, int start = 0, int end = -1)
        {
            var values = _client.LRange(listId, start, end);
            var result = new List<T>();

            foreach (var value in values)
            {
                result.Add(DeserializeValue<T>(value));
            }
            return result;
        }

        /// <summary>
        /// 获取整个列表（不移除）
        /// </summary>
        public List<T> ListGetAll<T>(string listId)
        {
            return ListRange<T>(listId, 0, -1);
        }

        #endregion

        #region 高级操作

        public bool KeyExpire(string key, TimeSpan expiry)
        {
            return _client.Expire(key, (int)expiry.TotalSeconds);
        }

        public bool KeyExists(string key)
        {
            return _client.Exists(key);
        }

        #endregion

        #region 发布订阅功能

        /// <summary>
        /// 发布消息到频道
        /// </summary>
        public long Publish(string channel, string message)
        {
            return _client.Publish(channel, message);
        }

        /// <summary>
        /// 发布消息到频道（支持对象序列化）
        /// </summary>
        public long Publish<T>(string channel, T message)
        {
            var serializedMessage = SerializeValue(message);
            return _client.Publish(channel, serializedMessage);
        }

        /// <summary>
        /// 订阅频道（异步方式，不会阻塞调用线程）
        /// </summary>
        /// <param name="channel">频道名称</param>
        /// <param name="onMessage">收到消息时的回调</param>
        /// <param name="onSubscribe">订阅成功时的回调</param>
        public void Subscribe(string channel, Action<string, string> onMessage, Action<string> onSubscribe = null)
        {
            var cts = new CancellationTokenSource();

            lock (_activeSubscriptions)
            {
                // 如果已经订阅，先取消
                if (_activeSubscriptions.ContainsKey(channel))
                {
                    _activeSubscriptions[channel].Cancel();
                }
                _activeSubscriptions[channel] = cts;
            }

            var subscribeObject = _client.Subscribe((channel, msg =>
            {
                if (!cts.Token.IsCancellationRequested)
                {
                    onMessage?.Invoke(channel, msg.Body);
                }
            }
            ));

            lock (_subscribeObjects)
            {
                _subscribeObjects[channel] = subscribeObject;
            }

            onSubscribe?.Invoke(channel);
        }

        /// <summary>
        /// 订阅频道（支持对象反序列化）
        /// </summary>
        public void Subscribe<T>(string channel, Action<string, T> onMessage, Action<string> onSubscribe = null)
        {
            Subscribe(channel,
                onMessage: (ch, msg) =>
                {
                    try
                    {
                        var deserializedMessage = DeserializeValue<T>(msg);
                        onMessage?.Invoke(ch, deserializedMessage);
                    }
                    catch (Exception ex)
                    {
                        throw new Exception($"反序列化消息失败: {ex.Message}");
                    }
                },
                onSubscribe: onSubscribe);
        }

        /// <summary>
        /// 取消订阅指定频道
        /// </summary>
        public void Unsubscribe(string channel)
        {
            lock (_activeSubscriptions)
            {
                if (_activeSubscriptions.TryGetValue(channel, out var cts))
                {
                    try
                    {
                        cts.Cancel();
                        _activeSubscriptions.Remove(channel);

                        // 释放订阅对象
                        lock (_subscribeObjects)
                        {
                            if (_subscribeObjects.TryGetValue(channel, out var subObj))
                            {
                                subObj?.Dispose();
                                _subscribeObjects.Remove(channel);
                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        throw new Exception($"取消订阅频道 {channel} 时发生错误: {ex.Message}");
                    }
                }
            }
        }

        /// <summary>
        /// 取消所有订阅
        /// </summary>
        public void UnsubscribeAll()
        {
            lock (_activeSubscriptions)
            {
                var channels = _activeSubscriptions.Keys.ToList();
                foreach (var channel in channels)
                {
                    Unsubscribe(channel);
                }
            }
        }

        /// <summary>
        /// 获取当前活跃的订阅列表
        /// </summary>
        public List<string> GetActiveSubscriptions()
        {
            lock (_activeSubscriptions)
            {
                return _activeSubscriptions.Keys.ToList();
            }
        }

        #endregion

        #region 键过期通知功能

        /// <summary>
        /// 订阅键过期事件（指定数据库）
        /// </summary>
        /// <param name="onKeyExpired">键过期时的回调，参数为过期的键名</param>
        /// <param name="database">数据库编号，默认0</param>
        /// <param name="keyPattern">键名模式，支持通配符，如 "user:*" 或 "*"</param>
        public void SubscribeKeyExpiration(Action<string> onKeyExpired, int database = 0, string keyPattern = "*")
        {
            try
            {
                // Redis键过期通知的频道格式：__keyevent@<db>__:expired
                string channel = $"__keyevent@{database}__:expired";

                var cts = new CancellationTokenSource();

                lock (_activeSubscriptions)
                {
                    // 如果已经订阅，先取消
                    if (_activeSubscriptions.ContainsKey(channel))
                    {
                        Unsubscribe(channel);
                    }
                    _activeSubscriptions[channel] = cts;
                }

                // CSRedis 订阅方式
                var subscribeObject = _client.Subscribe((channel, msg =>
                {
                    try
                    {
                        if (!cts.Token.IsCancellationRequested)
                        {
                            string key = msg.Body;

                            // 如果指定了键模式，进行过滤
                            if (keyPattern != "*" && !IsKeyMatchPattern(key, keyPattern))
                            {
                                return;
                            }
                            onKeyExpired?.Invoke(key);
                        }
                    }
                    catch (Exception ex)
                    {
                        throw new Exception($"[错误] 处理过期通知时出错: {ex.Message}");
                    }
                }
                ));

                lock (_subscribeObjects)
                {
                    _subscribeObjects[channel] = subscribeObject;
                }
            }
            catch (Exception ex)
            {
                throw new Exception($"[错误] 订阅键过期通知失败: {ex.Message}");
                throw;
            }
        }

        /// <summary>
        /// 订阅键过期事件（带详细信息）
        /// </summary>
        /// <param name="onKeyExpired">键过期时的回调，参数包含频道名和键名</param>
        /// <param name="database">数据库编号</param>
        /// <param name="keyPattern">键名模式</param>
        public void SubscribeKeyExpirationDetailed(Action<string, string> onKeyExpired, int database = 0, string keyPattern = "*")
        {
            try
            {
                string channel = $"__keyevent@{database}__:expired";

                var cts = new CancellationTokenSource();

                lock (_activeSubscriptions)
                {
                    if (_activeSubscriptions.ContainsKey(channel))
                    {
                        Unsubscribe(channel);
                    }
                    _activeSubscriptions[channel] = cts;
                }

                var subscribeObject = _client.Subscribe((channel, msg =>
                {
                    try
                    {
                        if (!cts.Token.IsCancellationRequested)
                        {
                            string key = msg.Body;

                            if (keyPattern != "*" && !IsKeyMatchPattern(key, keyPattern))
                            {
                                return;
                            }
                            onKeyExpired?.Invoke(msg.Channel, key);
                        }
                    }
                    catch (Exception ex)
                    {
                        throw new Exception($"[错误] 处理过期通知时出错: {ex.Message}");
                    }
                }
                ));

                lock (_subscribeObjects)
                {
                    _subscribeObjects[channel] = subscribeObject;
                }
            }
            catch (Exception ex)
            {
                throw new Exception($"[错误] 订阅失败: {ex.Message}");
            }
        }

        /// <summary>
        /// 订阅多个数据库的键过期事件
        /// </summary>
        /// <param name="onKeyExpired">键过期回调，参数为(数据库编号, 键名)</param>
        /// <param name="databases">要监听的数据库编号列表</param>
        /// <param name="keyPattern">键名模式</param>
        public void SubscribeMultiDatabaseKeyExpiration(Action<int, string> onKeyExpired, int[] databases, string keyPattern = "*")
        {
            foreach (var db in databases)
            {
                SubscribeKeyExpiration(
                    key => onKeyExpired?.Invoke(db, key),
                    db,
                    keyPattern
                );
            }
        }

        /// <summary>
        /// 取消键过期通知订阅
        /// </summary>
        /// <param name="database">数据库编号</param>
        public void UnsubscribeKeyExpiration(int database = 0)
        {
            string channel = $"__keyevent@{database}__:expired";
            Unsubscribe(channel);
        }

        /// <summary>
        /// 设置键并立即订阅其过期事件
        /// </summary>
        /// <param name="key">键名</param>
        /// <param name="value">值</param>
        /// <param name="expiry">过期时间</param>
        /// <param name="onExpired">过期回调</param>
        /// <param name="database">数据库编号</param>
        public bool SetWithExpiryNotification(string key, string value, TimeSpan expiry, Action<string> onExpired, int database = 0)
        {
            // 确保过期通知已启用
            // EnableKeyExpirationNotifications(); // 可选：自动启用

            // 设置键值
            bool success = Set(key, value, expiry);

            if (success)
            {
                // 订阅该键的过期事件（只订阅一次）
                string channel = $"__keyevent@{database}__:expired";

                lock (_activeSubscriptions)
                {
                    if (!_activeSubscriptions.ContainsKey(channel))
                    {
                        SubscribeKeyExpiration(onExpired, database, key);
                    }
                }
            }

            return success;
        }

        /// <summary>
        /// 批量设置键并订阅过期事件
        /// </summary>
        /// <param name="keyValues">键值对字典</param>
        /// <param name="expiry">过期时间</param>
        /// <param name="onExpired">过期回调</param>
        /// <param name="database">数据库编号</param>
        public void SetBatchWithExpiryNotification(Dictionary<string, string> keyValues, TimeSpan expiry, Action<string> onExpired, int database = 0)
        {
            foreach (var kvp in keyValues)
            {
                Set(kvp.Key, kvp.Value, expiry);
            }

            // 订阅所有键的过期事件
            SubscribeKeyExpiration(onExpired, database);
        }

        /// <summary>
        /// 检查键名是否匹配模式（简单通配符支持）
        /// </summary>
        private bool IsKeyMatchPattern(string key, string pattern)
        {
            if (pattern == "*") return true;

            // 简单的前缀匹配
            if (pattern.EndsWith("*"))
            {
                string prefix = pattern.TrimEnd('*');
                return key.StartsWith(prefix);
            }

            // 简单的后缀匹配
            if (pattern.StartsWith("*"))
            {
                string suffix = pattern.TrimStart('*');
                return key.EndsWith(suffix);
            }

            // 精确匹配
            return key == pattern;
        }

        #endregion

        #region 键空间通知（Keyspace Notifications）扩展功能

        /// <summary>
        /// 订阅键删除事件
        /// </summary>
        public void SubscribeKeyDeletion(Action<string> onKeyDeleted, int database = 0)
        {
            string channel = $"__keyevent@{database}__:del";

            var cts = new CancellationTokenSource();
            lock (_activeSubscriptions)
            {
                if (_activeSubscriptions.ContainsKey(channel))
                {
                    Unsubscribe(channel);
                }
                _activeSubscriptions[channel] = cts;
            }

            var subscribeObject = _client.Subscribe((channel, msg =>
            {
                if (!cts.Token.IsCancellationRequested)
                {
                    onKeyDeleted?.Invoke(msg.Body);
                }
            }
            ));

            lock (_subscribeObjects)
            {
                _subscribeObjects[channel] = subscribeObject;
            }
        }

        /// <summary>
        /// 订阅键设置事件
        /// </summary>
        public void SubscribeKeySet(Action<string> onKeySet, int database = 0)
        {
            string channel = $"__keyevent@{database}__:set";

            var cts = new CancellationTokenSource();
            lock (_activeSubscriptions)
            {
                if (_activeSubscriptions.ContainsKey(channel))
                {
                    Unsubscribe(channel);
                }
                _activeSubscriptions[channel] = cts;
            }

            var subscribeObject = _client.Subscribe((channel, msg =>
            {
                if (!cts.Token.IsCancellationRequested)
                {
                    onKeySet?.Invoke(msg.Body);
                }
            }
            ));

            lock (_subscribeObjects)
            {
                _subscribeObjects[channel] = subscribeObject;
            }
        }

        #endregion

        public void Dispose()
        {
            if (!_disposed)
            {
                UnsubscribeAll();
                _client?.Dispose();
                _disposed = true;
            }
        }

        #region 类型转换辅助方法

        private string SerializeValue<T>(T value)
        {
            if (value == null) return null;

            if (value is string strValue)
                return strValue;

            // 简单类型直接ToString
            Type valueType = typeof(T);
            if (valueType == typeof(int) || valueType == typeof(long) ||
                valueType == typeof(double) || valueType == typeof(bool) ||
                valueType == typeof(decimal) || valueType == typeof(float))
                return value.ToString();

            // 复杂对象使用JSON序列化
            try
            {
                return JsonSerializer.Serialize(value);
            }
            catch
            {
                return value.ToString();
            }
        }

        private T DeserializeValue<T>(string value)
        {
            if (string.IsNullOrEmpty(value))
                return default(T);

            // 字符串类型直接返回
            if (typeof(T) == typeof(string))
                return (T)(object)value;

            try
            {
                // 尝试JSON反序列化
                return JsonSerializer.Deserialize<T>(value);
            }
            catch
            {
                // JSON反序列化失败，尝试基本类型转换
                try
                {
                    return (T)Convert.ChangeType(value, typeof(T));
                }
                catch
                {
                    return default(T);
                }
            }
        }

        #endregion
    }
}
